﻿using System;
using System.Threading;
using log4net;

namespace Jiubang.Threads {
    /// <summary>
    ///   封装<see cref = "T:System.Threading.Timer" />类，
    ///   使Change方法可设置超时回调并捕获回调方法中未处理的异常。
    /// </summary>
    internal sealed class ActionChangableTimer<T> : IDisposable {
        private static readonly ILog _log = LogManager.GetLogger(typeof(ActionChangableTimer<T>));
        private readonly Timer _timer;
        private Action<T> _onTimeout;
        private T _state;

        /// <summary>
        ///   初始化回调及计时信息
        /// </summary>
        public ActionChangableTimer() {
            _timer = new Timer(InternalCallback);
        }

        #region IDisposable Members

        /// <summary>
        ///   回收timer占用的资源
        /// </summary>
        public void Dispose() {
            _timer.Dispose();
        }

        #endregion

        private void InternalCallback(object state) {
            try {
                _onTimeout(_state);
            } catch (Exception e) {
                _log.Warn(e);
            }
        }

        /// <summary>
        /// 更新回调及计时信息
        /// </summary>
        /// <param name = "action">超时回调方法。当dueTime不是-1时该值不能为null</param>
        /// <param name = "state"></param>
        /// <param name = "dueTime">首次启动延迟毫秒数。0为立刻启动。</param>
        /// <param name = "period">首次启动后循环执行间隔毫秒数。</param>
        public void Change(Action<T> action, T state, int dueTime, int period) {
            if (dueTime != -1 && action == null)
                throw new ArgumentNullException();

            _onTimeout = action;
            _state = state;
            _timer.Change(dueTime, period);
        }

        /// <summary>
        /// 放弃计时任务
        /// </summary>
        public void Reset() {
            Change(null, default(T), Timeout.Infinite, Timeout.Infinite);
        }
    }
}